切换主题
获取真实 ip
更新时间: 10/30/2024字数: 0 字
公开版:https://www.hackjie.com/tracking
自己实现版本:https://codepen.io/cc-Jimmy/pen/RwzzEpL?editors=1111
测试反馈:
- 代理软件使用 Tun 模式,全部失效
- iPhone 全部浏览器失效
- 代理软件不使用 Tun 模式,能正常拿到真实 ip 地址
webrtc 技术分析:
WebRTC
是一个开源项目,通过简单的 API 为 Web 浏览器和移动的应用程序提供实时通信功能。
WebRTC
支持浏览器之间的音频、视频和数据流,允许开发人员构建视频聊天、文件传输和屏幕共享等应用程序,而无需依赖第三方插件或扩展。
WebRTC
由几个关键组件组成,这些组件共同提供实时通信:
getUserMedia
:允许访问用户的网络摄像头和麦克风。RTCPeerConnection
:管理建立和维护对等点之间连接的整个过程。RTCDataChannel
:支持对等点之间的双向数据传输,允许低延迟通信。WebRTC
依赖于几种协议来建立和维护对等体之间的连接:- ICE(Interactive Connectivity Establishment):通过 NAT(Network Address Translators)和防火墙连接对等体的框架,ICE 使用 STUN 和 TURN 服务器来发现和中继网络信息。
- STUN(Session Traffic Utilities for NAT):一种允许客户端发现其公有 IP 地址和所处 NAT 类型的协议。
- TURN(使用 NAT 周围的中继进行传输):为由于 NAT 限制或防火墙而无法建立直接连接的对等体提供中继服务器的协议。
需要注意的是,使用WebRTC
获取用户 IP 地址可能会引发隐私问题。在收集任何信息(包括 IP 地址)之前,必须通知您的用户并征得他们的同意。
要使用WebRTC
获取用户的 IP 地址,您可以利用 ICE 流程,该流程在对等点之间交换网络信息。在 ICE 过程中,浏览器收集本地 IP 地址并生成包含此信息的 ICE 候选。
创建 RTCPeerConnection
使用 ICE 服务器创建一个
RTCPeerConnection
:
ts
const iceServers = [
{
urls: 'stun:stun.l.google.com:19302'
}
]
const peerConnection = new RTCPeerConnection({ iceServers })
这个连接对象表示两个对等体之间的连接,负责管理数据交换。
“stun:stun.l.google.com:19302
”是由 Google 提供的公共 STUN 服务器,它是一个高度可靠的免费公共 STUN 服务器。如果需要,您可以选择使用自己的 STUN 服务器。
创建数据通道
对于某些浏览器(如 Chrome),创建数据通道是触发 ICE 进程所必需的。使用
createDataChannel
方法创建数据通道tspeerConnection.createDataChannel('')
监听
onicecandidate
事件以在生成 ICE 候选时收集 IP 地址tspeerConnection.onicecandidate = (event) => { console.log('RTC Peer Connection Ice Event:', event) }
触发 ICE 调用
createOffer
和setLocalDescription
方法触发 ICE 进程tspeerConnection .createOffer() .then((offer) => peerConnection.setLocalDescription(offer)) .catch((error) => console.error('Error creating offer:', error))
提取 IP 地址
每个 ICE 候选包含关于潜在网络路径的信息,包括 IP 地址和端口。
所以在
onicecandidate
中,创建一个正则表达式来提取 IP 地址tsconst ipv4Regex = /\b((25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\b/ const ipv6Regex = /\b(?:[A-F0-9]{1,4}:){7}[A-F0-9]{1,4}\b/i const ipSet = new Set() const onicecandidate = (ice: RTCPeerConnectionIceEvent) => { const candidate = ice?.candidate?.candidate if (candidate) { for (const regex of [ipv4Regex, ipv6Regex]) { const [ip] = candidate.match(regex) ?? [] if (ip) { ipSet.add(ip) } } } }
完整版代码
ts
const ipv4Regex =
/\b((25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\b/
const ipv6Regex = /\b(?:[A-F0-9]{1,4}:){7}[A-F0-9]{1,4}\b/i
// prettier-ignore
// @ts-expect-error
globalThis.RTCPeerConnection = window.RTCPeerConnection || window.mozRTCPeerConnection || window.webkitRTCPeerConnection
const publicIPs = (timeout = 1000) => {
const ipSet = new Set()
const onicecandidate = (ice: RTCPeerConnectionIceEvent) => {
const candidate = ice?.candidate?.candidate
if (candidate) {
for (const regex of [ipv4Regex, ipv6Regex]) {
const [ip] = candidate.match(regex) ?? []
if (ip) {
ipSet.add(ip)
}
}
}
}
return new Promise((resolve, reject) => {
const conn = new globalThis.RTCPeerConnection({
iceServers: [
{
urls: 'stun:stun.l.google.com:19302'
}
]
})
conn.addEventListener('icecandidate', onicecandidate)
conn.createDataChannel('')
conn.createOffer().then((offer) => conn.setLocalDescription(offer), reject)
setTimeout(() => {
try {
conn.removeEventListener('icecandidate', onicecandidate)
conn.close()
} catch {
// ignore
}
resolve([...ipSet])
}, timeout)
})
}
publicIPs().then(() => {})